#include <signal.h>
#include <sys/stat.h>

#include "binary.h"
#include "logger.h"

int g_logsignal = LOGLEVEL_DEBUG;

void set_log_flag(int signo)
{
    if(10 == signo)
    {
        ++g_logsignal; g_logsignal %= LOGLEVEL_COUNT;
    }
}

#ifdef WIN32

int set_signal(int signo)
{  
    if(0 == signo)
    {
        return -1;
    }

    set_log_flag(signo);

    return 0;
}

#else

typedef void signal_func(int);

signal_func* set_signal_handler(int signo, signal_func *func)
{
    struct sigaction act, oact;

    act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0;

    if (sigaction(signo, &act, &oact) < 0)
        return SIG_ERR;
    else
        return oact.sa_handler;
}

int set_signal(int signo)
{
    if(0 == signo)
    {
        return -1;
    }

    if(set_signal_handler(signo, set_log_flag) == SIG_ERR)
    {
        return -2;
    }

    return 0;
}

#endif

extern string g_exepath;

extern string g_exename;

void parse_moudle_path();

string replace_separator(const char* inbuf, const char srcdelim, const char destdelim);

static const int max_log_file_size = 3 * 1024 * 1024;

CLog::CLog(const char* filename)
: m_file(NULL)
, m_index(0)
{
    if(g_exepath.empty()) parse_moudle_path();

    m_name = g_exepath + LOG_DIR; m_name += filename;
}

CLog::~CLog()
{
    if(NULL != m_file)
    {
        fclose(m_file); m_file = NULL;
    }
}

void CLog::Logout(int level, const char* file, int line, const char* func, char* msg, ...)
{
    if(g_logsignal < level) return;

    string subfile = replace_separator(file, '\\', '/');
    
    subfile = subfile.substr(subfile.find_last_of('/') + 1);

    char* subfunc = (char*)strrchr(func, ':');

    char curtime[20] = { 0 }; get_cur_time(curtime, 20);

    char buffer[3*1024] = { 0 };

    int length = arg_desc(buffer, 3*1024, "-->Time:%s,Level:%d,File:%s,Line:%u,Func:%s,Log:", &curtime[11], level, subfile.c_str(), line, (NULL!=subfunc)?(++subfunc):func);

    if(length > 0 && length < 3*1024)
    {
        va_list args;
        va_start(args, msg);
        vsnprintf(&buffer[length], 3*1024-length-1, msg, args);
        va_end(args);
    }
    
    m_lock.Lock();
    
    char commond[1024] = { 0 }; int filesize = 0; bool isfilelost = false;
    
    sprintf(commond, "%s_%s_%d.log", m_name.c_str(), m_time.c_str(), m_index);

    struct stat statbuf; if(0 == stat(commond, &statbuf)) { filesize = statbuf.st_size; }

    if(access(commond, 0) != 0) isfilelost = true;

    curtime[10] = '-'; curtime[13] = '\0';

    if(isfilelost || NULL == m_file || strncmp(m_time.c_str(), curtime, 13) != 0 || filesize > max_log_file_size)
    {
        if(strncmp(m_time.c_str(), curtime, 13) != 0)
        {
            m_index = 0; m_time = curtime;
        }

        sprintf(commond, "%s_%s_%d.log", m_name.c_str(), m_time.c_str(), ++m_index);

        if(NULL != m_file)  fclose(m_file);

        m_file = fopen(commond, "a+");

        if(NULL != m_file)
        {
            fprintf(m_file, "====================================================\n");
            fprintf(m_file, "== LEVEL->0:NOLOG,1:NOTICE,2:ERROR,3:INFO,4:DEBUG ==\n");
            fprintf(m_file, "====================================================\n");
            fflush(m_file);
        }
    }
  
    if(NULL != m_file)
    {
        fprintf(m_file, "%s\n", buffer); fflush(m_file);

#ifdef WIN32
        printf("%s\n", buffer);
#endif
    }

    m_lock.Unlock();
}

CLogger::CLogger()
: m_syslog(NULL)
{
}

CLogger::~CLogger()
{
    LOG_MAP::iterator it = m_logmap.begin();

    while(it != m_logmap.end())
    {
        delete (it->second); m_logmap.erase(it);

        it = m_logmap.begin();
    }
}

CLog* CLogger::GetSysLog()
{
    if(NULL == m_syslog)
        m_syslog = GetLog("_system");

    return m_syslog;
}

CLog* CLogger::GetLog(const char* name)
{
    CLog* log = NULL;

    m_lock.Lock();

    LOG_MAP::iterator it = m_logmap.find(name);

    if(it != m_logmap.end())
    {
        log = (*it).second;
    }
    else
    {
        log = new CLog(name);

        if(NULL != log) m_logmap.insert(std::make_pair(name, log));
    }

    m_lock.Unlock();

    return log;
}
